home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 11 / Cream of the Crop 11-1.iso / comm / ytsg3.zip / YTSG.C < prev    next >
C/C++ Source or Header  |  1995-12-25  |  43KB  |  998 lines

  1. /************************************************************************
  2.  * ytsg.c - Convery Yarn files to SOUP offline reader format using grep
  3.  *          syntax to select messages.
  4.  * Copyright (C) 1995, Richard Curry Consulting: trindflo@fishnet.net
  5.  * All Rights Reserved
  6.  *
  7.  * My software is generally available as free software under the terms
  8.  * of the GNU General Public License.
  9.  *
  10.  * Parts of this software were copied from source code made available as
  11.  * part of IBMs Developer's Connection.  IBM copyright information has
  12.  * been retained in this file.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program in a file name 'COPYING'; if not, write to the
  21.  * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  *
  23.  * SUMMARY:
  24.  *    This file contains the code to translate/copy Yarn news files into
  25.  *    soup offline reader format.  This initially is intended to allow
  26.  *    cleanup of a corrupted Yarn newsbase.
  27.  *
  28.  *    To build: See build.cmd: expects IBM CSET/2 compiler.
  29.  *
  30.  *    To use:  see below
  31.  *
  32.  * REVISION HISTORY:
  33.  *
  34.  *   Date          Version  By    Purpose of Revision
  35.  * --------------- ------- -----  ---------------------------------------
  36.  * Thu  95-04-06    1.00    RRC   Initial Draft
  37.  * Mon  95-12-25    3.00    RRC   Handle X-Newsgroups correctly
  38.  *
  39.  ************************************************************************/
  40.  
  41. /************************************************/                         
  42. /* Author: G.R. Blair                           */
  43. /*         BOBBLAIR @ AUSVM1                    */
  44. /*         bobblair@bobblair.austin.ibm.com     */
  45. /*                                              */
  46. /* The following code is property of            */
  47. /* International Business Machines Corporation. */
  48. /*                                              */
  49. /* Copyright International Business Machines    */
  50. /* Corporation, 1991.  All rights reserved.     */
  51. /************************************************/
  52. /* Identification String to be put at the top of each AIXLIKE utility */
  53. char zqrzqvjb[] = "AIXLIKE V3.0 by Bob Blair - Copyright (c) 1990, 1992, IBM";
  54.  
  55. /*-----------------------------------------------------------------------*/
  56. /* Modification History:                                                 */
  57. /*      Release 1       5/1/91                                           */
  58. /* @1 05.08.91 changed fmf_init to find all files, even hidden           */
  59. /* @2 10.19.91 upper casing a line of over 256 bytes caused Trap D       */
  60. /* @3 05.22.92 failed on eof condition (trap d) reported by B. Kwan      */
  61. /* @4 05.03.93 modified for IBM C/Set2 compiler                          */
  62. /*-----------------------------------------------------------------------*/
  63.  
  64. /* ytsg: searches a file for a pattern.
  65.  
  66.    Usage:
  67.           ytsg [-v][-o][-i] {Pattern | -e Pattern | -f StringFile} [YarnFile]
  68. */
  69.  
  70. /* The ytsg command searches the input file specified by the *YarnFile*
  71. parameter (standard input by default) for news/mail messages matching a
  72. pattern.  The ytsg command searches specifically for *Pattern*
  73. parameters that are fixed strings.  The ytsg command outputs the message
  74. containing the matched line to soup formatted files.  The pattern that
  75. you are matching must be in the first 50,000 bytes of the file.
  76. A real regular expression parser here might be nice, but for now it is
  77. not to be.
  78.  
  79. The exit values of this command are:
  80.   0   A match was found
  81.   1   No match was found
  82.   2   A syntax error was found or a file was inaccessible (even if matches were
  83.       found).
  84.  
  85. Flags:
  86.       -e Pattern    Specifies a pattern.  This works the same as a simple
  87.                     pattern, but is useful when a pattern begins with a -.
  88.  
  89.       -f StringFile Specifies a file that contains strings to be matched.
  90.                     (The file may specify up to 256 strings in this
  91.                     implementation).
  92.  
  93.       -i            Ignores the case of letters when making comparisons.
  94.  
  95.       -o            Outputs the HEX offset of each message.  First message is
  96.                     at offset 0.
  97.  
  98.       -v            Output all messages except those that match the specified
  99.                     pattern.
  100. */
  101.  
  102. /* -------------------------------------------------------------------------- */
  103. /*  Maintenance history:                                                      */
  104. /*           Jan 15 1991: Fixed case-insensitive search                       */
  105. /*                                                                            */
  106. /*                                                                            */
  107. /*                                                                            */
  108. /*                                                                            */
  109. /*                                                                            */
  110. /* -------------------------------------------------------------------------- */
  111.  
  112. #include <stdlib.h>
  113. #include <stdio.h>
  114. #include <ctype.h>
  115. #include <string.h>
  116. #include <io.h>
  117. #include <fcntl.h>
  118. #include <sys/types.h>
  119. #include <sys/stat.h>
  120. #include <errno.h>
  121. #define  INCL_BASE
  122. #define  INCL_NOPM
  123. #include <os2.h>
  124. #include "fmf.h"
  125.  
  126. // #define BUFSIZE    30000
  127. #define BUFSIZE    65536
  128. #define MAXPATTERNS  256
  129. #define MAXLINE 255
  130. #define MATCHFOUND     0
  131. #define NOMATCHFOUND   1
  132. #define SOMEERROR      2
  133. #define YES            1
  134. #define NO             0
  135. #define BAILOUT       -1
  136. #define INIT           0
  137. #define NEXT           1
  138. #define TAB           0x09
  139. #define CR            0x0d
  140. #define LF            0x0a
  141. #define STDIN          0
  142. #define NONPOLISH      0
  143. #define REVERSE        1
  144. #define BUFEND         (buf + BUFSIZE)
  145.  
  146. char *optstring = "e:f:iov";           /* valid option letters */
  147. char *stdinC = "STDIN";
  148. char *pattern[MAXPATTERNS] = {NULL};     /* up to MAXPATTERNS pattern ptrs */
  149.  
  150.                    /*----------------------------*/
  151.                    /* Options and their defaults */
  152.                    /*----------------------------*/
  153. char *filespec = NULL;          /* file(s) to be searched */
  154. char *StringFile = NULL;        /* name of file containing patterns */
  155. int  logic = NONPOLISH;         /* search for matching or non-matching files? */
  156. int  subtreesrch = NO;          /* search subtrees for matching files ? */
  157. int  casesensitive = YES;       /* respect case in comparing to pattern? */
  158. int  showhexoffset = NO;        /* show hex offset with matching message? */
  159. ULONG ulOffset;                  /* ftell for current message start */
  160.  
  161. char readbuf[BUFSIZE];                  /* Work buffer                        */
  162. char inp_buf[65536];                    /* vbuf for speeding stdio access     */
  163. char out_buf[65536];                    /* vbuf for speeding stdio access     */
  164.  
  165. char szNewsGps[4096];                   /* String of newsgroups for this msg  */
  166. char szCurrentGroup[1024];              /* Current group messages are sent to */
  167.  
  168. unsigned long ulMsgByteCount;           /* # of bytes in current message      */
  169. unsigned long ulBytesToCopy;            /* # of bytes still to be copied      */
  170. unsigned long ulBytesThisTime;          /* # of bytes to copy this iteration  */
  171. unsigned long ulActualRead;             /* # of bytes actually read           */
  172. unsigned long ulActualWrite;            /* # of bytes actually written        */
  173. int retval;                             /* Return value from setvbuf          */
  174. int fHaveGp;                            /* Flag implies there is a current gp */
  175. unsigned short usMsgNum = 0;            /* Filename: 0000001.MSG, 0000002...  */
  176.  
  177.         /* Define file access structures */
  178. FILE *inp_file;
  179. FILE *out_file;
  180. FILE *pfAreas, *pfNewscr;
  181.  
  182.         /* Define constant strings */
  183. char *szAreasID = "AREAS";
  184. char szNewsrcID[512];
  185.  
  186.  
  187.  
  188.                    /*----------------------------*/
  189.                    /*    Function Prototypes     */
  190.                    /*----------------------------*/
  191. int init(int argc, char *argv[]);   /* Initialize from command line data */
  192. int getpatterns(char *filename);    /* get patterns from string file */
  193. void do_a_file(char *filename);     /* Logic for converting file to stream */
  194. void do_STDIN(void);                /* Logic when input file is stdin      */
  195.                                     /* Logic for matching lines in one file */
  196. void do_the_stream(FILE *stream, char *filename);
  197. void tell_usage(void);              /* Explain program usage */
  198.                                     /* Parse command line */
  199. int  getopt(int argc, char *argv[], char *opstring);
  200.  
  201. extern int optind;                  /* data exported by getopt() */
  202. extern char *optarg;
  203.                                     /* put out errors in a standard format */
  204. void myerror(int rc, char *area, char *details);
  205. extern char *myerror_pgm_name;      /* data imported by myerror */
  206.  
  207. int rexpres(char *pBuf, ULONG cbBuff);  /* Look for strings in buffer         */
  208. int env_init(void);                 /* Grab required files, env. strings, etc */
  209. void errex(char *mess_ptr);         /* Output and error and exit              */
  210. int showmsg(void);                  /* Output a 'found' indicator             */
  211.                                     /* Get a copy of 'Newsgroup:' string      */
  212. int CopyNewsGroupToString( char *pDest, ULONG MaxSizeDest );
  213. int SetupMail( void );              /* Allow for 'mail' and newsgroups        */
  214. int SetupNewAREA( char *szNewGpName );  /* Sets up new '.MSG' file            */
  215. int SubscribedTo( char *szTarget ); /* Checks to see if newsgroup in newsrc   */
  216. int swal(unsigned char *adrs);      /* Swap 32 bits end-to-end                */
  217. int fgetrsp(FILE *pf, char *bufr, int max); /* Read a line, discard >max      */
  218. int strnlzap(char *sp);             /* Find newline and NULL terminate at NL  */
  219. int finish_input(FILE *pf);         /* Read file until newline                */
  220.  
  221.  
  222. int  rtrnstatus = NOMATCHFOUND;     /* value returned on exit from ytsg */
  223.  
  224. /*----------------------------------------------------------------------------*/
  225. /*  main                                                                      */
  226. /*                                                                            */
  227. /*  mainline logic:                                                           */
  228. /*      Perform initialization                                                */
  229. /*      For each filespec on the command line                                 */
  230. /*         Initialize a search for files that match the spec                  */
  231. /*         For every file that does match the spec                            */
  232. /*            Try to match the pattern                                        */
  233. /*         Close the search for matching files to prepare for the next one.   */
  234. /*                                                                            */
  235. /*  None of this holds true if no filespec was specified.  In that case we    */
  236. /*  take input from STDIN and try to match that.                              */
  237. /*----------------------------------------------------------------------------*/
  238. int main(int argc, char *argv[])
  239. {
  240.    char filename[CCHMAXPATH];
  241.    int  attrib, i, filespecindx, rc;
  242.  
  243.    myerror_pgm_name = "ytsg";
  244.    if ( env_init() == BAILOUT )         /* Locate necessary files, env. etc   */
  245.      return(SOMEERROR);
  246.    if ( (filespecindx = init(argc, argv)) == BAILOUT)
  247.      return(SOMEERROR);
  248.    else
  249.      if (filespecindx > 0)
  250.        {
  251.          for (i = filespecindx; i < argc; i++)
  252.            {
  253.             if ( (rc = fmf_init(argv[i], FMF_ALL_FILES, subtreesrch))
  254.                                                           == FMF_NO_ERROR )
  255.               {
  256.                 while (fmf_return_next(filename, &attrib) == NO_ERROR)
  257.                   do_a_file(filename);
  258.                 fmf_close();
  259.               }
  260.             else
  261.               myerror(rc, "Finding filespec", argv[i]);
  262.            }
  263.        }
  264.      else
  265.        do_STDIN();
  266.    return(rtrnstatus);
  267. }
  268.  
  269. /*----------------------------------------------------------------------------*/
  270. /*  env_init                                                                  */
  271. /*                                                                            */
  272. /*  Do initialization:                                                        */
  273. /*      Open files and check for environment strings which are necessary for  */
  274. /*      the operation of this program.                                        */
  275. /*                                                                            */
  276. /*----------------------------------------------------------------------------*/
  277. int env_init(void) {
  278.  
  279.   if ( getenv("HOME") == NULL )         /* Need HOME to find newsrc           */
  280.     errex("HOME environment variable must be set to find newsrc file.\n");
  281.  
  282.   strcpy(szNewsrcID, getenv("HOME"));   /* Copy HOME address into name base   */
  283.   strcat(szNewsrcID, "\\yarn\\newsrc.");  /* add filename string              */
  284.  
  285.   pfNewscr = fopen(szNewsrcID , "r");   /* Open list of subscribed newsgroups */
  286.   if (pfNewscr == NULL) {
  287.     printf("Attempting to open newsrc file at %s ", szNewsrcID);
  288.     perror("");
  289.     exit(3);
  290.   }
  291.  
  292.   if ( access(szAreasID,0) == 0 )       /* If this file exists                */
  293.     errex("AREAS file already exists. Terminating to avoid corruption.\n");
  294.  
  295.   pfAreas = fopen(szAreasID, "wb");     /* Open list of AREAS (part of SOUP)  */
  296.   if (pfAreas == NULL)
  297.     errex("Can't open AREAS file.\n");
  298.  
  299.  
  300.   fHaveGp = FALSE;                      /* Flag no current group              */
  301.   return YES;
  302. }
  303.  
  304. /*----------------------------------------------------------------------------*/
  305. /*  init                                                                      */
  306. /*                                                                            */
  307. /*  Do initialization:                                                        */
  308. /*      Examine command line options and set internal variables accordingly.  */
  309. /*      If a pattern wasn't specified with -f, take if from the next command  */
  310. /*       line position; complain if there isn't one.                          */
  311. /*      If there are remaining command line arguments, they are file specs.   */
  312. /*                                                                            */
  313. /*                                                                            */
  314. /*                                                                            */
  315. /*                                                                            */
  316. /*----------------------------------------------------------------------------*/
  317. int init(int argc, char *argv[])
  318. {
  319.     int rtrnindx = 0;
  320.     int args, i;
  321. #ifdef I16
  322.     char c;
  323. #else
  324.     int c;
  325. #endif
  326.     char *p;
  327.     int cu;
  328.  
  329.     for (i = 0; i < MAXPATTERNS; pattern[i++] = NULL); /* initialize patterns */
  330. #ifdef I16
  331.     while ( (c = (char)getopt(argc, argv, optstring)) != EOF)
  332. #else
  333.     while ( (c = getopt(argc, argv, optstring)) != EOF)
  334. #endif
  335.     {
  336.  
  337.         cu = toupper(c);
  338.         switch (cu)
  339.         {
  340.             case 'V':    logic = REVERSE;
  341.                break;
  342.             case 'I':    casesensitive = NO;
  343.                break;
  344.             case 'O':    showhexoffset = YES;
  345.                break;
  346.             case 'E':    if (StringFile == NULL)
  347.                            pattern[0] = optarg;
  348.                          else
  349.                            {
  350.                               tell_usage();
  351.                               return(BAILOUT);
  352.                            }
  353.                break;
  354.             case 'F':    if (pattern[0] == NULL)
  355.                            StringFile = optarg;
  356.                          else
  357.                            {
  358.                               tell_usage();
  359.                               return(BAILOUT);
  360.                            }
  361.                break;
  362.             default:     tell_usage();
  363.                          return(BAILOUT);
  364.                break;
  365.  
  366.         } /* endswitch */
  367.     }
  368.  
  369.     args = optind;
  370.     if ( (pattern[0] == NULL) && (StringFile == NULL) )
  371.       if  (args < argc)
  372.         pattern[0] = argv[args++];
  373.  
  374.     if (StringFile != NULL)
  375.       if (pattern[0] == NULL)
  376.         {
  377.           if (getpatterns(StringFile) == BAILOUT)
  378.             return(BAILOUT);
  379.         }
  380.       else
  381.         {
  382.           tell_usage();
  383.           return(BAILOUT);
  384.         }
  385.  
  386.     if ( (pattern[0] == NULL) && (StringFile == NULL) )
  387.       {
  388.         tell_usage();
  389.         return(BAILOUT);
  390.       }
  391.  
  392.     if (args < argc)
  393.       rtrnindx = args++;
  394.  
  395.     if (casesensitive == NO)  /* upper case the patterns if search is */
  396.       if (pattern != NULL)              /* insensitive */
  397.         for (i = 0; pattern[i]; i++)
  398.           for (p = pattern[i]; *p; p++)
  399.             *p = (char)toupper(*p);
  400.     return(rtrnindx);
  401. }
  402.  
  403. /*----------------------------------------------------------------------------*/
  404. /*  getpatterns                                                               */
  405. /*                                                                            */
  406. /*                                                                            */
  407. /*                                                                            */
  408. /*                                                                            */
  409. /*                                                                            */
  410. /*----------------------------------------------------------------------------*/
  411. int getpatterns(char *filename)
  412. {
  413.    FILE *stream;
  414.    char buff[MAXLINE], *p, *q;
  415.    int i;
  416.  
  417.    i = 0;
  418.    if ( (stream = fopen(filename, "r")) != NULL)           /* @4c */
  419.      {
  420.        while (fgets(buff, MAXLINE, stream) != NULL)
  421.          {
  422.            if (i >= MAXPATTERNS)
  423.              {
  424.                fprintf(stderr,
  425.                "Only the first %d patterns in the pattern file will be used\n",
  426.                MAXPATTERNS);
  427.                break;
  428.              }
  429.            if ( (pattern[i] = (char *)malloc(strlen(buff) + 1)) != NULL )
  430.              {
  431.                for (p = pattern[i], q = buff;
  432.                     *q && (*q != CR) && (*q != LF);
  433.                     *p++ = *q++);
  434.                *p = '\0';
  435.                i++;
  436.              }
  437.            else
  438.              {
  439.                myerror(ERROR_NOT_ENOUGH_MEMORY, "getting space for patterns", "");
  440.                return(BAILOUT);
  441.              }
  442.          }
  443.        fclose(stream);
  444.      }
  445.    else
  446.      {
  447.        printf("Could not open StringFile %s\n", filename);
  448.        return(BAILOUT);
  449.      }
  450.    return(YES);
  451. }
  452. /*----------------------------------------------------------------------------*/
  453. /*  do_a_file                                                                 */
  454. /*                                                                            */
  455. /*                                                                            */
  456. /*                                                                            */
  457. /*                                                                            */
  458. /*                                                                            */
  459. /*                                                                            */
  460. /*                                                                            */
  461. /*                                                                            */
  462. /*                                                                            */
  463. /*                                                                            */
  464. /*----------------------------------------------------------------------------*/
  465. void do_a_file(char *filename)
  466. {
  467.     FILE *stream;
  468.  
  469.     if ( (stream = fopen(filename, "rb"))  != NULL)
  470.         {
  471.           retval=setvbuf(stream, inp_buf, _IOFBF, sizeof(inp_buf) );
  472.           if (retval!=0) errex("Unable to set input buffer\n");
  473.  
  474.           do_the_stream(stream, filename);
  475.           fclose(stream);
  476.         }
  477.       else
  478.         {
  479.           rtrnstatus = SOMEERROR;
  480. //          perror(filename);
  481.           myerror(0, "File open error", filename);
  482.         }
  483. }
  484.  
  485. /*----------------------------------------------------------------------------*/
  486. /*  do_STDIN                                                                  */
  487. /*                                                                            */
  488. /*  Same thing as do_a_file(), really, except that we already know the handle.*/
  489. /*                                                                            */
  490. /*----------------------------------------------------------------------------*/
  491. void do_STDIN()
  492. {
  493.    FILE *stream;
  494.  
  495.    if ( (stream = fdopen(STDIN, "rb"))  != NULL)
  496.       do_the_stream(stream, stdinC);
  497.    else
  498.       {
  499.         rtrnstatus = SOMEERROR;
  500. //        perror("stdin->stream");
  501.         myerror(0, "Opening STDIN as stream", "");
  502.       }
  503. }
  504.  
  505. /*----------------------------------------------------------------------------*/
  506. /*  do_the_stream                                                             */
  507. /*                                                                            */
  508. /*                                                                            */
  509. /*                                                                            */
  510. /*                                                                            */
  511. /*                                                                            */
  512. /*                                                                            */
  513. /*                                                                            */
  514. /*                                                                            */
  515. /*                                                                            */
  516. /*                                                                            */
  517. /*----------------------------------------------------------------------------*/
  518. void do_the_stream(FILE *inp_file, char *filename)
  519. {
  520.   printf("Extracting messages from file %s: ", filename);
  521.  
  522.   for (;;) {                            /* Until end of file                  */
  523.     if (feof(inp_file)) break;
  524.  
  525.         /* Read the byte count */
  526.     if ( fread(&ulMsgByteCount, 1, 4, inp_file) != 4 ) {
  527.       if (feof(inp_file)) break;
  528.       else {
  529.         perror("Error reading from input file");
  530.         exit(3);
  531.       }                                         
  532.     }
  533.  
  534.     if ( (ulMsgByteCount & 0xffff0000) != 0 ) { /* > 64K in one message       */
  535.       printf("Message length of %ld found\n", ulMsgByteCount);
  536.     }
  537.  
  538.     ulBytesToCopy = ulMsgByteCount;
  539.     ulBytesThisTime = min(ulBytesToCopy, sizeof(readbuf));
  540.  
  541.     ulOffset = ftell(inp_file);
  542.  
  543.         /* Read up to 64K of the message */
  544.     ulActualRead = fread(readbuf, 1, ulBytesThisTime, inp_file);
  545.     if ( ulActualRead != ulBytesThisTime ) {
  546.       if (feof(inp_file)) {
  547.         perror("Abnormal end of input file");
  548.         printf("Requested: %ld, Actual: %ld -- proceed [Y/n]? ",
  549.                                                 ulBytesThisTime, ulActualRead);
  550.         if ( tolower(getchar()) == 'n' ) exit(3);
  551.             /* Fill remainder with ^Z */
  552.         memset(readbuf+ulActualRead, 26, ulBytesThisTime-ulActualRead);
  553.       }
  554.       else {
  555.         perror("Error reading from input file");
  556.         exit(3);
  557.       }                                         
  558.     }
  559.  
  560.     if ( !rexpres(readbuf, ulActualRead) ) {  /* Look for a string match      */
  561.  
  562.         /* No match, clean up and move on to next message */
  563.       ulBytesToCopy -= ulBytesThisTime; /* Tally the bytes so far             */
  564.  
  565.       while ( ulBytesToCopy!=0 ) {      /* Complete the read of the message   */
  566.  
  567.           /* Read up to 64K of the message */
  568.         ulBytesThisTime = min(ulBytesToCopy, sizeof(readbuf));
  569.         ulActualRead = fread(readbuf, 1, ulBytesThisTime, inp_file);
  570.         if ( ulActualRead != ulBytesThisTime ) {
  571.           if (feof(inp_file)) {
  572.             perror("Abnormal end of input file in unselected message");
  573.             printf("Requested: %ld, Actual: %ld\n",
  574.                                                   ulBytesThisTime, ulActualRead);
  575.           }
  576.           else {
  577.             perror("Error reading from input file in unselected message");
  578.             exit(3);
  579.           }
  580.         }
  581.  
  582.         ulBytesToCopy -= ulBytesThisTime; /* Tally the successful read        */
  583.       } /* While more than 64K worth of message needs to be skipped */
  584.  
  585.       continue;                         /* Done.  Read next message           */
  586.     }
  587.  
  588.     showmsg();                          /* Indicate activity: message selected*/
  589.  
  590.     if ( !CopyNewsGroupToString(szNewsGps, sizeof(szNewsGps)) )
  591.       *szNewsGps = '\0';                /* Mail message then                  */
  592.  
  593.         /* If we have established a group, and hence a .MSG file */
  594.     if (fHaveGp) {
  595.  
  596.         /* If we are change in or out of mail mode */
  597.       if ( ((*szNewsGps & *szCurrentGroup) == 0) &&
  598.            ((*szNewsGps | *szCurrentGroup) != 0) ) {
  599.  
  600.         fclose(out_file);             /* New group, close current file      */
  601.         fHaveGp = FALSE;
  602.       }
  603.  
  604.         /* Check to see if the current message is part of this group */
  605.       else if ( strstr(szNewsGps, szCurrentGroup) == NULL )  {
  606.  
  607.         fclose(out_file);               /* New group, close current file      */
  608.         fHaveGp = FALSE;
  609.       }
  610.     }
  611.  
  612.         /* If we need to establish a group to put this message in */
  613.     if (!fHaveGp) {
  614.       char *szFirstGp, *szTestGp;
  615.  
  616.       if ( *szNewsGps == '\0' ) {       /* Switch into mail mode              */
  617.  
  618.         szTestGp = szNewsGps;           /* Point to NULL message              */
  619.         SetupMail();                    /* Handle SOUP protocol               */
  620.       }
  621.  
  622.       else {                            /* Normal mail group                  */
  623.  
  624.         /* Check each group in newsgroups list for a group we subscribe to */
  625.         szFirstGp = strtok(szNewsGps," ,");
  626.         for ( szTestGp=szFirstGp; szTestGp!=NULL; szTestGp=strtok(NULL," ,") )
  627.           if (SubscribedTo(szTestGp)) break;
  628.  
  629.         /* If there is no group we are subscribed to, go with the first one */
  630.         if ( szTestGp == NULL ) szTestGp = szFirstGp;
  631.  
  632.         SetupNewAREA(szTestGp);         /* Handle SOUP protocol               */
  633.       }
  634.  
  635.       strcpy(szCurrentGroup, szTestGp);
  636.       fHaveGp = TRUE;
  637.     }
  638.  
  639.         /* Identify the new message in SOUP format */
  640.     if ( *szCurrentGroup == '\0' ) {    /* If in mail mode, mail MSG format   */
  641.       unsigned long ulTemp;
  642.  
  643.       ulTemp = ulMsgByteCount;
  644.       swal((unsigned char *)&ulTemp);
  645.       fwrite(&ulTemp, 1, 4, out_file);
  646.     }
  647.     else                                /* Else in usenet mode                */
  648.       fprintf(out_file, "#! rnews %ld\n", ulMsgByteCount);
  649.  
  650.         /* Write whatever we previously read */
  651.     ulActualWrite = fwrite(readbuf, 1, ulBytesThisTime, out_file);
  652.     if ( ulActualWrite != ulBytesThisTime ) {
  653.       perror("Error writing to output file");
  654.       exit(3);
  655.     }
  656.     ulBytesToCopy -= ulBytesThisTime;   /* Tally the successful write         */
  657.  
  658.     while ( ulBytesToCopy!=0 ) {        /* Complete the copy of the message   */
  659.  
  660.         /* Read up to 64K of the message */
  661.       ulBytesThisTime = min(ulBytesToCopy, sizeof(readbuf));
  662.       ulActualRead = fread(readbuf, 1, ulBytesThisTime, inp_file);
  663.       if ( ulActualRead != ulBytesThisTime ) {
  664.         if (feof(inp_file)) {
  665.           perror("Abnormal end of input file");
  666.           printf("Requested: %ld, Actual: %ld -- Writing actual\n",
  667.                                                 ulBytesThisTime, ulActualRead);
  668.             /* Fill remainder with ^Z */
  669.           memset(readbuf+ulActualRead, 26, ulBytesThisTime-ulActualRead);
  670.         }
  671.         else {
  672.           perror("Error reading from input file");
  673.           exit(3);
  674.         }
  675.       }
  676.  
  677.         /* Write whatever we just read */
  678.       if (ulBytesThisTime != 0) {
  679.         ulActualWrite = fwrite(readbuf, 1, ulBytesThisTime, out_file);
  680.         if ( ulActualWrite != ulBytesThisTime ) {
  681.           perror("Error writing extended records to output file");
  682.           exit(3);
  683.         }
  684.       }
  685.       ulBytesToCopy -= ulBytesThisTime; /* Tally the successful write         */
  686.     } /* While more than 64K worth of message needs to be copies */
  687.   } /* For entire input file */
  688.  
  689. }
  690.  
  691. /*----------------------------------------------------------------------------*/
  692. /*  rexpres(Buffer, BytesInBuffer)                                            */
  693. /*    Search Buffer for any of the search strings.  Account for command       */
  694. /*    flags.  Return TRUE if found, FALSE if not.                             */
  695. /*                                                                            */
  696. /*----------------------------------------------------------------------------*/
  697. int rexpres(char *pBuf, ULONG cbBuff) {
  698.   int match = NO;                       /* Collect an answer                  */
  699.   char **pp;                            /* Array of pointers                  */
  700.  
  701.   if ( *pattern[0] == '*' ) match = YES;  /* Only wildcard in first version   */
  702.  
  703.   else for (pp=pattern; !match && *pp; pp++) {  /* For every pattern          */
  704.     char *residue = pBuf;               /* Sliding window through buffer      */
  705.     ULONG cbResidue = cbBuff;           /* # of bytes in the remaining tail   */
  706.     char *pFirstChar;                   /* Search pointer                     */
  707.  
  708.     if (casesensitive) {                /* Simple case...look for exact str   */
  709.  
  710.       while(cbResidue) {                /* While there are chars in the tail  */
  711.         ULONG cbSS;                     /* Bytes in search string             */
  712.  
  713.         pFirstChar = memchr(residue, **pp, cbResidue);
  714.         if (pFirstChar == NULL) break;  /* First char not found, no match     */
  715.  
  716.         cbResidue -= (pFirstChar - residue);  /* Get size of remaining buffer */
  717.         residue = pFirstChar;           /* Update pointer -- slide window     */
  718.  
  719.         cbSS = strlen(*pp);             /* Read total # chars in buffer       */
  720.         if ( cbSS > cbResidue) break;   /* Can not possibly match now         */
  721.  
  722.         if ( memcmp(*pp, residue, cbSS) == 0 ) {  /* If an exact match        */
  723.           match = YES;
  724.           break;
  725.         }
  726.  
  727.         cbResidue--; residue++;         /* Not the start of a match, try next */
  728.       } /* While searching the buffer */
  729.     } /* If case sensitive */
  730.  
  731.     else {                              /* Case insensitive -- complicated    */
  732.  
  733.       while(cbResidue) {                /* While there are chars in the tail  */
  734.         char *pBuffer, *pSrch;          /* Work pointers                      */
  735.         char *pLower, *pUpper;          /* Additional search pointers         */
  736.         int cbSrch;                     /* Bytes in search string             */
  737.  
  738.             /* Check for first character in either case */
  739.         pUpper = memchr(residue, **pp, cbResidue);
  740.         pLower = memchr(residue, tolower(**pp), cbResidue);
  741.  
  742.         pFirstChar = min(pLower, pUpper);   /* Go with lowest address         */
  743.         if (!pFirstChar)                /* If one of the addresses was NULL   */
  744.           pFirstChar = max(pLower, pUpper); /* Go with the other              */
  745.         if (!pFirstChar)                /* If both of the addresses are NULL  */
  746.           break;                        /* Nothing of interest left in buf    */
  747.  
  748.         cbResidue -= (pFirstChar - residue);  /* Get size of remaining buffer */
  749.         residue = pFirstChar;           /* Update pointer -- slide window     */
  750.  
  751.         match = YES;                    /* Now assume match was found         */
  752.         for (pBuffer=residue, pSrch=*pp, cbSrch=cbResidue;
  753.               *pSrch && cbSrch; pBuffer++, pSrch++, cbSrch-- )
  754.                                         /* And search a char at a time        */
  755.           if ( toupper(*pBuffer) != *pSrch)   /* For a mismatch               */
  756.             break;
  757.  
  758.         if ( *pSrch ) match = NO;       /* Search string was not exhausted    */
  759.  
  760.         if (match) break;
  761.  
  762.         cbResidue--; residue++;         /* Not the start of a match, try next */
  763.       } /* While searching the buffer */
  764.     } /* else case insensitive */
  765.   } /* else for every pattern until a match */
  766.  
  767.   if (logic == REVERSE) match = !match; /* handle reversing logic             */
  768.   return match;
  769. }
  770.  
  771.  
  772. /******************************************************************************/
  773. /* Major subroutines                                                          */
  774. /******************************************************************************/
  775.  
  776. /*----------------------------------------------------------------------------*/
  777. /*  SubscribedTo: Search the newsrc file in an attempt to locate szTarget     */
  778. /*  as a subscribed newsgroup.  Checks for (!) unsubscribed groups.           */
  779. /*----------------------------------------------------------------------------*/
  780. char szNewsrcLine[1024];
  781.  
  782. int SubscribedTo( char *szTarget ) {
  783.   char *pName;
  784.  
  785.   fseek(pfNewscr, 0, SEEK_SET);         /* Point to top of file               */
  786.  
  787.         /* While there is a line to get, read beginning of line */
  788.   while ( fgetrsp(pfNewscr, szNewsrcLine, sizeof(szNewsrcLine)-1) )
  789.   {
  790.     pName = strstr( szNewsrcLine, szTarget );
  791.     if ( pName == NULL ) continue;      /* Not this line, try another         */
  792.  
  793.     pName += strlen(szTarget);          /* Point just past the string         */
  794.     while ( (*pName == ' ') || (*pName == '\t') ) pName++;  /* Skip whitespace*/
  795.  
  796.     if (*pName == ':') return TRUE;     /* This is a group we subscribe to    */
  797.     if (*pName == '!') return FALSE;    /* This is a group we 'unsubscribed'  */
  798.       /* Or...it was something else! */
  799.   }
  800.  
  801.   return FALSE;                         /* Never found this group             */
  802. }
  803.  
  804. /*----------------------------------------------------------------------------*/
  805. /*  SetupNewAREA: Open a new MSG file, make entry in AREAS file, etc.         */
  806. /*----------------------------------------------------------------------------*/
  807. int SetupNewAREA( char *szNewGpName ) {
  808.   char szOutFileName[16];
  809.  
  810.         /* Log new MSG file to AREAS */
  811.   fprintf(pfAreas, "%07d\t%s\tun\n", ++usMsgNum, szNewGpName);
  812.  
  813.         /* Generate filename, open and setup output file */
  814.   sprintf(szOutFileName, "%07d.MSG", usMsgNum);
  815.  
  816.   out_file = fopen(szOutFileName, "wb");
  817.   if (out_file == NULL)
  818.     errex("Can't open new output file.\n");
  819.  
  820.   retval=setvbuf(out_file, out_buf, _IOFBF, sizeof(out_buf) );
  821.   if (retval!=0) errex("Unable to set output buffer\n");
  822.  
  823.   return TRUE;
  824. }
  825.  
  826. /*----------------------------------------------------------------------------*/
  827. /*  SetupMail: Open a new MSG file for mail, etc.                             */
  828. /*----------------------------------------------------------------------------*/
  829. int SetupMail( void ) {
  830.   char szOutFileName[16];
  831.  
  832.         /* Log new MSG file to AREAS */
  833.   fprintf(pfAreas, "%07d\tEmail\tbn\n", ++usMsgNum);
  834.  
  835.         /* Generate filename, open and setup output file */
  836.   sprintf(szOutFileName, "%07d.MSG", usMsgNum);
  837.  
  838.   out_file = fopen(szOutFileName, "wb");
  839.   if (out_file == NULL)
  840.     errex("Can't open new output file.\n");
  841.  
  842.   retval=setvbuf(out_file, out_buf, _IOFBF, sizeof(out_buf) );
  843.   if (retval!=0) errex("Unable to set output buffer\n");
  844.  
  845.   return TRUE;
  846. }
  847.  
  848.  
  849. /*----------------------------------------------------------------------------*/
  850. /*  CopyNewsGroupToString: Locate 'Newsgroups:' string and copy to a buffer.  */
  851. /*  must leave original to be copies to MSG file.                             */
  852. /*----------------------------------------------------------------------------*/
  853. char szNewsgpID[] = "Newsgroups:";
  854.  
  855. int CopyNewsGroupToString( char *pDest, ULONG MaxSizeDest ) {
  856.   char *pGps, *pGpsEnd, *pGpsFault;
  857.   unsigned long ulGpsLen;
  858.  
  859.   pGps = readbuf;                       /* Initialize search pointer          */
  860.   for (;;) {                            /* Search for correct or NULL string  */
  861.  
  862.     pGps = strstr( pGps, szNewsgpID );
  863.     if ( pGps == NULL ) return FALSE;   /* No list of groups found            */
  864.     if ( (pGps == readbuf) ||           /* If string starts at top            */
  865.          ( *(pGps-1) == '\n' ) ||       /* OR the beginning of a line         */
  866.          ( *(pGps-1) == ' ' ) )         /* OR is not part of a larger string  */
  867.       break;                            /* Then we have our string            */
  868.     pGps++;                             /* Otherwise, search again            */
  869.   }
  870.  
  871.       /* Check for end of header (Two \n's in a row) before newsgroup string */
  872.   pGpsFault = strstr( readbuf, "\n\n" );
  873.   if ( (pGpsFault!=NULL)  && (pGps > strstr(readbuf, "\n\n")) ) return FALSE;
  874.  
  875.   if ( (pGps != readbuf) &&             /* If string did not start at top     */
  876.        ( *(pGps-1) != '\n' ) ) {        /* AND did not start line             */
  877.     printf("Warning, 'Newsgroups:' identifier not at beginning of line\n");
  878.   }
  879.  
  880.   pGps += sizeof(szNewsgpID)-1;         /* Look past the Identifier           */
  881.  
  882.   pGpsEnd = strchr(pGps, '\n');         /* Find end of newsgroups line        */
  883.   if ( pGpsEnd == NULL ) return FALSE;  /* No end of line???                  */
  884.  
  885.   ulGpsLen = pGpsEnd-pGps;              /* Calculate the length of the line   */
  886.   ulGpsLen = min(ulGpsLen, MaxSizeDest-1);  /* Limit the size of the string   */
  887.  
  888.   memcpy(pDest, pGps, ulGpsLen);        /* Copy the string                    */
  889.   *(pDest+ulGpsLen) = '\0';             /* NULL terminate                     */
  890.  
  891.   return TRUE;
  892. }
  893.  
  894.  
  895. /******************************************************************************/
  896. /* Minor subroutines                                                          */
  897. /******************************************************************************/
  898.  
  899. /*----------------------------------------------------------------------------*/
  900. /*  fgetrsp: read a line.  Chars beyond 'max' are discarded.  Entire line is  */
  901. /*  guaranteed read.  Buffer is guaranteed NULL terminated.  Returns FALSE if */
  902. /*  no line is read (presumably end of file).                                 */
  903. /*----------------------------------------------------------------------------*/
  904. int fgetrsp(FILE *pf, char *bufr, int max) {
  905.  
  906.   if ( fgets(bufr, max, pf) == NULL )   /* Get a 'line'                       */
  907.     return FALSE;                       /* Normal termination mode            */
  908.  
  909.   if (!strnlzap(bufr))                  /* Attempt to kill terminating newline*/
  910.     finish_input(pf);                   /* Not found...complete reading line  */
  911.  
  912. return TRUE;
  913. }
  914.  
  915. /*----------------------------------------------------------------------------*/
  916. /*  strnlzap: Look for newline in a buffer replace it with NULL (zap it).     */
  917. /*  return TRUE if newline was found, FALSE if not.                           */
  918. /*----------------------------------------------------------------------------*/
  919. int strnlzap(char *sp) {
  920.  
  921.   while (*sp != '\n')                   /* Look for the newline               */
  922.     if (!(*sp++))                       /* If we hit end of string, rtn FALSE */
  923.       return FALSE;
  924.   *sp = 0;                              /* Kill string at newline */
  925.   return TRUE;                          /* Flag that we did our job */
  926. }
  927.  
  928. /*----------------------------------------------------------------------------*/
  929. /*  finish_input: read the file until newline or EOF is read.                 */
  930. /*----------------------------------------------------------------------------*/
  931. char zaparray[256];
  932. int finish_input(FILE *pf)
  933. {
  934.   for (;;) {
  935.     if ( fgets(zaparray, sizeof(zaparray), pf) == NULL ) return FALSE;
  936.     if ( strchr(zaparray, '\n') ) return TRUE;
  937.   }
  938. }
  939.  
  940. /*----------------------------------------------------------------------------*/
  941. /*  swal: Swap a long end to end (from big to little endian).                 */
  942. /*  call with the pointer to the long to be converted.                        */
  943. /*----------------------------------------------------------------------------*/
  944. int swal(unsigned char *adrs) {
  945.   unsigned char tempbyte;
  946.  
  947.   tempbyte  = *(adrs+3);
  948.   *(adrs+3) = *adrs;
  949.   *adrs++   = tempbyte;
  950.   tempbyte  = *(adrs+1);
  951.   *(adrs+1) = *adrs;
  952.   *adrs     = tempbyte;
  953.   return YES;
  954. }
  955.  
  956.  
  957.  
  958. /*----------------------------------------------------------------------------*/
  959. /*  errex: Prints an error message to the screen and does 'serious' error exit*/
  960. /*----------------------------------------------------------------------------*/
  961. void errex(char *mess_ptr) {
  962.   printf(mess_ptr);
  963.   exit(3);
  964. }
  965.  
  966. /*----------------------------------------------------------------------------*/
  967. /*  showmsg : Give user a warm fuzzy when we select a message.  If -o option  */
  968. /*    then print HEX offset of the message within source file.                */
  969. /*                                                                            */
  970. /*----------------------------------------------------------------------------*/
  971. int showmsg(void) {
  972.   if (showhexoffset)
  973.         printf("0x%lX-", ulOffset);
  974.   else  putchar('.');
  975.   return YES;
  976. }
  977.  
  978. /*----------------------------------------------------------------------------*/
  979. /*  tell_usage                                                                */
  980. /*                                                                            */
  981. /*----------------------------------------------------------------------------*/
  982. void tell_usage()
  983. {
  984.   printf("\n      Adapted from software provided by IBM, Copyright 1990, 1992\n");
  985.   printf("ytsg searches a Yarn newsbase for a pattern and makes SOUP.\n\n");
  986.   printf("Usage:\n");
  987.   printf("    ytsg [-vio] {Pattern | -e Pattern | -f StringFile} [YarnFile]\n");
  988.  
  989.   printf("\nFlags:\n");
  990.   printf("    -e Pattern    Specifies a pattern.\n");
  991.   printf("    -f StringFile Specifies a file that contains strings to be matched.\n");
  992.   printf("    -i            Ignores the case of letters when making comparisons.\n");
  993.   printf("    -o            Output the offset of each msg as found\n");
  994.   printf("    -v            Displays all lines except those that match pattern.\n");
  995.   printf("    -y            Ignore case of letters when making comparisons.\n");
  996. }
  997.  
  998.